package kercar.robot.serialcom;

import gnu.io.CommPortIdentifier;
import gnu.io.SerialPort;
import gnu.io.SerialPortEvent;
import gnu.io.SerialPortEventListener;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.Enumeration;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import kercar.compass.Compass;
import kercar.debug.Debugger;
import kercar.gps.GPSPosition;
import kercar.manager.CompassManager;
import kercar.manager.GPSManager;

public class SerialRead implements SerialPortEventListener {
	
	/************************************************************************* BLOCK
	 * 		ATTRIBUTES
	 *******************************************************************************/
	
	private static final String[] PORT_NAMES = { "/dev/tty.usbserial-A9007UX1", "/dev/ttyUSB0", "COM4" };
	private static final int TIME_OUT = 2000;
	private static final int DATA_RATE = 9600;
	private static final String PATTERN_GPS = "gps\\?lat=(?<LATDEGRES>.*) (?<LATMINUTES>.*);lon=(?<LONDEGRES>.*) (?<LONMINUTES>.*);heading=(?<HEADING>.*);",
								PATTERN_COMPASS = "compass\\?angle=(?<COMPANGLE>.*);";
	
	private SerialPort _serialPort;
	private GPSManager _gpsManager;
	private CompassManager _compManager;
	private BufferedReader _input;
	private OutputStream _output;

	/************************************************************************* BLOCK
	 * 		PUBLIC LIBRARY METHODS
	 *******************************************************************************/
	
	public void initialize() {
		CommPortIdentifier portId = null;
		
		@SuppressWarnings("rawtypes")
		Enumeration portEnum = CommPortIdentifier.getPortIdentifiers();

		while (portEnum.hasMoreElements()) {
			CommPortIdentifier currPortId = (CommPortIdentifier) portEnum.nextElement();
			for (String portName : PORT_NAMES) {
				if (currPortId.getName().equals(portName)) {
					portId = currPortId;
					break;
				}
			}
		}
		
		if (portId == null) {
			System.out.println("Could not find COM port.");
			return;
		}

		try {
			_serialPort = (SerialPort) portId.open(this.getClass().getName(), TIME_OUT);

			_serialPort.setSerialPortParams(DATA_RATE, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);

			_input = new BufferedReader(new InputStreamReader(_serialPort.getInputStream()));
			_output = _serialPort.getOutputStream();

			_serialPort.addEventListener(this);
			_serialPort.notifyOnDataAvailable(true);

		} catch (Exception e) {
			System.err.println(e.toString());
		}
	}

	public synchronized void close() {
		if (_serialPort != null) {
			_serialPort.removeEventListener();
			_serialPort.close();
		}
	}

	public synchronized void serialEvent(SerialPortEvent oEvent) {
		//if (oEvent.getEventType() == SerialPortEvent.DATA_AVAILABLE) {
			try {
				String inputLine = _input.readLine();
				saveState(inputLine);
			} catch (Exception e) {
				System.err.println("Erreur 1 : " + e.toString());
			}
		//}
	}

	public synchronized void write(String msg) {
		try {
			_output.write(msg.getBytes());
		} catch (IOException e) {
			Debugger.logException(e);
		}
	}
	
	/************************************************************************* BLOCK
	 * 		PRIVATE METHODS
	 *******************************************************************************/
	
	private synchronized void saveState(String input) {
		Pattern GPSPattern = Pattern.compile(PATTERN_GPS);
		Matcher GPSMatcher = GPSPattern.matcher(input);

		Pattern CompassPattern = Pattern.compile(PATTERN_COMPASS);
		Matcher CompassMatcher = CompassPattern.matcher(input);
		
		if (GPSMatcher.matches()) {
			double lat = Double.parseDouble(GPSMatcher.group("LATDEGRES")) + (Double.parseDouble(GPSMatcher.group("LATMINUTES"))) / 60;
			double lon = Double.parseDouble(GPSMatcher.group("LONDEGRES")) + (Double.parseDouble(GPSMatcher.group("LONMINUTES"))) / 60;
			_gpsManager.setPosition(new GPSPosition(lat, lon));
		}
		else if (CompassMatcher.matches()) {
			double compass = Double.parseDouble(CompassMatcher.group("COMPANGLE"));
			_compManager.setCompass(new Compass(compass));
		}
	}
	
	/************************************************************************* BLOCK
	 * 		GETTERS & SETTERS
	 *******************************************************************************/
	
	public void setGPSManager(GPSManager gpsManager) {
		_gpsManager = gpsManager;
	}
	
	public void setCompassManager(CompassManager compManager) {
		_compManager = compManager;
	}
	
}
